/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkClass, ViewTreeNode } from 'arkanalyzer/lib';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from '../BaseChecker';
import { Rule, Defects, ClassMatcher, MatcherTypes, MatcherCallback } from '../../Index';
import { IssueReport } from '../../model/Defects';
import { ViewTreeTool } from '../../utils/checker/ViewTreeTool';

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'StackComponentNestingCheck');

const gMetaData: BaseMetaData = {
    severity: 1,
    ruleDocPath: 'docs/stack-component-nesting-check.md',
    description: 'Avoid using Stack component with exactly 2 children (Image and Text) or multiple children containing both Image and Text for simple stacking scenarios.'
};

// 字符串常量定义
const STACK_COMPONENT_NAME = 'Stack';
const CREATE_ATTRIBUTE = 'create';
const ALLOWED_CHILD_COMPONENTS = ['Image', 'Text'];

let viewTreeTool: ViewTreeTool = new ViewTreeTool();

export class StackComponentNestingCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public issues: IssueReport[] = [];

    private clsMatcher: ClassMatcher = {
        matcherType: MatcherTypes.CLASS,
        hasViewTree: true
    };

    public registerMatchers(): MatcherCallback[] {
        return [{
            matcher: this.clsMatcher,
            callback: this.check
        }];
    }

    public check = (arkClass: ArkClass): void => {
        const viewTreeRoot = arkClass.getViewTree()?.getRoot();
        if (!viewTreeRoot) {
            return;
        }
        this.traverseViewTree(viewTreeRoot, arkClass, this.handleViewTreeNode.bind(this));
    };

    private traverseViewTree(viewTreeNode: ViewTreeNode, arkClass: ArkClass, callback: (node: ViewTreeNode, arkClass: ArkClass) => void): void {
        if (viewTreeTool.hasTraverse(viewTreeNode)) {
            return;
        }

        callback(viewTreeNode, arkClass);

        // 继续遍历子节点
        if (viewTreeNode.children && viewTreeNode.children.length > 0) {
            for (let child of viewTreeNode.children) {
                // 跳过自定义组件，避免重复检查
                if (child.isCustomComponent()) {
                    continue;
                }
                this.traverseViewTree(child, arkClass, callback);
            }
        }
    }

    private handleViewTreeNode(viewTreeNode: ViewTreeNode, arkClass: ArkClass): void {
        if (!viewTreeNode) {
            return;
        }

        // 检查当前节点是否为 Stack 组件
        if (viewTreeNode.name === STACK_COMPONENT_NAME) {
            this.checkStackChildren(viewTreeNode, arkClass);
        }
    }

    private checkStackChildren(stackNode: ViewTreeNode, arkClass: ArkClass): void {
        const children = stackNode.children;
        if (!children || children.length === 0) {
            return;
        }

        // 只要同时包含 Image 和 Text 就告警
        const hasImage = children.some(child => child.name === 'Image');
        const hasText = children.some(child => child.name === 'Text');
        if (hasImage && hasText) {
            this.reportIssue(stackNode, arkClass);
        }
    }

    private reportIssue(stackNode: ViewTreeNode, arkClass: ArkClass): void {
        const createStmts = stackNode.attributes.get(CREATE_ATTRIBUTE);
        if (!createStmts) {
            return;
        }

        const stmt = createStmts[0];
        const filePath = arkClass.getDeclaringArkFile().getFilePath();
        const severity = this.rule.alert ?? this.metaData.severity;

        // 使用getOriginPositionInfo获取位置信息
        const reportPosition = stmt.getOriginPositionInfo();
        if (!reportPosition) {
            return;
        }

        const line = reportPosition.getLineNo();
        const startCol = reportPosition.getColNo();
        const endCol = startCol + STACK_COMPONENT_NAME.length - 1;

        const defect = new Defects(
            line,
            startCol,
            endCol,
            this.metaData.description,
            severity,
            this.rule.ruleId,
            filePath,
            this.metaData.ruleDocPath,
            true,
            false,
            false
        );

        this.issues.push(new IssueReport(defect, undefined));
    }
}
